﻿using GXWA_DATA.Model.ConfigModel;
using GXWA_DATA.Model.Singleton;
using GXWA_DATA.Utils;
using S7.Net;
using System.Threading.Tasks;

/// <summary>
/// PLC交互帮助类
/// </summary>
public class PlcConnectionManager
{

    #region 线程事件变量
    Task _connectTask;
    private CancellationTokenSource _cts;
    #endregion

    /// <summary>
    /// 启动连接PLC线程
    /// </summary>
    /// <returns></returns>
    public async Task ConnectToPlc()
    {
        _cts = new CancellationTokenSource();

        var token = _cts.Token;

        foreach (var item in PlcConfigModelSingleton.Instance)
        {
            _connectTask = Task.Run(async () =>
            {
                try
                {
                    Plc plc = item.plc;

                    Tools.DeviceLogText(item.Config.Nick + "正在连接........");

                    await plc.OpenAsync(token);

                    item.IsConnected = true;

                    Tools.DeviceLogText(item.Config.Nick + "已连接成功");

                    //启动心跳线程
                    item.heartBeatTask = Task.Run(() => sendHeartBeat(item, token));
                }
                catch (Exception ex)
                {
                    //打印失败日志
                    Tools.DeviceLogTextError(item.Config.Nick + ":" + ex.Message);

                    //启动重连线程
                    ReconnectToPlc(item, token);
                }
            }, token);

            await Task.Delay(10);
        }
    }

    /// <summary>
    /// 心跳线程
    /// </summary>
    /// <param name="plcConfig"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    private async Task sendHeartBeat(PlcConfigModel plcConfig, CancellationToken token)
    {
        Tools.DeviceLogText($"启动：{plcConfig.Config.Nick} 心跳线程");

        while (plcConfig.IsConnected && !token.IsCancellationRequested)
        {
            try
            {
                bool beat = false;

                while (true)
                {
                    plcConfig.plc.WriteBit(DataType.DataBlock, 1300, 65, 1, beat);

                    beat = !beat;

                    await Task.Delay(plcConfig.Config.HeartBeat);
                }
            }
            catch (Exception ex)
            {
                plcConfig.IsConnected = false;

                Tools.DeviceLogTextError("发送心跳包：" + plcConfig.Config.Nick + "失败:" + ex.Message);

                //启动重连线程
                ReconnectToPlc(plcConfig, token);

                break;
            }
        }
    }

    /// <summary>
    /// 重连线程
    /// </summary>
    /// <param name="plcConfig"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    private async void ReconnectToPlc(PlcConfigModel plcConfig, CancellationToken token)
    {
        Tools.DeviceLogText($"启动：{plcConfig.Config.Nick} 重连线程");
        bool logIsOut = false;
        while (!plcConfig.IsConnected && !token.IsCancellationRequested)
        {
            try
            {
                await plcConfig.plc.OpenAsync(token);

                plcConfig.IsConnected = true;

                //启动心跳线程
                plcConfig.heartBeatTask = Task.Run(() => sendHeartBeat(plcConfig, token));

                break;
            }
            catch (Exception ex)
            {
                plcConfig.IsConnected = false;

                if (!logIsOut)
                    Tools.DeviceLogTextError($"{plcConfig.Config.Nick}重连线程启动失败，原因是{ex.Message}");

                logIsOut = true;
            }
            await Task.Delay(plcConfig.Config.RetryInterval);
        }
    }
    #region 自定义方法

    #endregion

}